package com.fans.admin.quartz.util;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.TimeZone;

import org.quartz.CronExpression;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.quartz.impl.matchers.GroupMatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.toolkit.CollectionUtils;
import com.fans.admin.quartz.constant.QtzConstant.ScheduleStatus;
import com.fans.admin.quartz.entity.ScheduleJob;
import com.fans.admin.quartz.job.QuartzJobFactory;

/**
 * 任务工具
 * 
 * @author fanhaohao
 *
 */
public class QuartzUtil {
	
	private static Logger logger = LoggerFactory.getLogger(QuartzUtil.class);
	
	public static final TimeZone DEFAULT_TIME_ZONE = TimeZone.getTimeZone("GMT+8:00");
	
	/**
	 * 检验表达式
	 * @param cronExpression 如：0 0/1 * * * ?
	 * @return
	 * @throws SchedulerException 
	 */
	public static Boolean checkCronExpression(String cronExpression) throws SchedulerException {
		Boolean flag = true;
		try {  
            CronExpression exp = new CronExpression(cronExpression);  
            Date d = new Date();  
            exp.getNextValidTimeAfter(d);  
        } catch (ParseException e) {  
        	logger.error("表达式【" + cronExpression + "】错误(" + e.getMessage() + ")");
        	logger.error(ExceptionUtil.getStackTraceAsString(e));
            throw new SchedulerException("表达式【" + cronExpression + "】错误(" + e.getMessage() + ")");
        }  
		return flag;
	}
	
	/**
	 * 检验表达式
	 * @param cronExpression 如：0 0/1 * * * ?
	 * @return
	 * @throws SchedulerException 
	 */
	public static Date getCronExpressionResult(Date startTime, String cronExpression) throws SchedulerException {
		try {  
            CronExpression exp = new CronExpression(cronExpression);  
            return exp.getNextValidTimeAfter(startTime);  
        } catch (ParseException e) {  
        	logger.error("表达式【" + cronExpression + "】错误(" + e.getMessage() + ")");
        	logger.error(ExceptionUtil.getStackTraceAsString(e));
            throw new SchedulerException("表达式【" + cronExpression + "】错误(" + e.getMessage() + ")");
        }  
	}
	
	/**
	 * 判断是否存在任务
	 * 
	 * @param scheduler
	 * @param jobId
	 * @return
	 */
	public static Boolean isExistScheduler(Scheduler scheduler, String jobId) {
		//判断是否存在
		TriggerKey triggerKey = TriggerKey.triggerKey(jobId);
    	
    	//获取trigger，即在spring配置文件中定义的 bean id="myTrigger"
    	CronTrigger trigger;
		try {
			trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
	    	
	    	//如果不存在，则创建；否则，修改参数与表达式
	    	if(null == trigger) {    	
	    		return false;
	    	} 
		} catch (SchedulerException e) {
			// TODO Auto-generated catch block
        	logger.error(ExceptionUtil.getStackTraceAsString(e));
		}
		return true;
	}
	
    /**
	 * 创建任务
	 * 
	 * @param scheduler
	 *            调度工厂
	 * @param jobId
	 *            任务Id
	 * @param cronExpression
	 *            运行时间表达式
	 * @param isSync
	 *            是否同步
	 * @param paramKey
	 *            参数key，运行时可以通过paramKey获得param参数
	 * @param param
	 *            参数
	 * @throws SchedulerException
	 */
	public static void createScheduleJob(Scheduler scheduler, ScheduleJob job, boolean isContextInitialized)
			throws SchedulerException {
        String cronExpression = job.getCronExpression();
    	//判断是否存在
		TriggerKey triggerKey = TriggerKey.triggerKey(job.getId());
    	
    	//获取trigger，即在spring配置文件中定义的 bean id="myTrigger"
    	CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
    	
    	//如果不存在，则创建；否则，修改参数与表达式
    	if(null == trigger) {    	
	        //同步或异步
	        Class<? extends Job> jobClass = QuartzJobFactory.class;
	        
	        //构建job信息
			JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(job.getId()).build();
	        
	        /**
	         * 放入参数，运行时的方法可以获取；
	         * 注：使用数据库存储JobDetail的时候（默认情况下使用RAM），
	         * 不能把没有实现java.io.Serializable的对象放入JobDataMap中，
	         * 因为Quartz将使用Blob字段保存（也可以通过配置文件关闭）序列化过的JobDataMap中的对象
	         */
			// String paramKey = job.getId();
	        //jobDetail.getJobDataMap().put(paramKey, job.getId());
	        
	        //表达式调度构建器
	        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
	        
	        scheduleBuilder.inTimeZone(DEFAULT_TIME_ZONE);
	        
	        //按新的cronExpression表达式构建一个新的trigger
			trigger = TriggerBuilder.newTrigger().withIdentity(job.getId())
	            .withSchedule(scheduleBuilder).build();
	        
	        try {
	        	//调度工厂，调度触发器
	            scheduler.scheduleJob(jobDetail, trigger);
	        } catch (SchedulerException e) {
				logger.error("创建【" + job.getJobName() + "】任务失败！");
	        	logger.error(ExceptionUtil.getStackTraceAsString(e));
				throw new SchedulerException("创建【" + job.getJobName() + "】异常：\n" + e.getMessage());
	        }
    	} else {
    		if(!isContextInitialized) {
				logger.error("任务【" + job.getJobName() + "】已经在运行！");
				throw new SchedulerException("任务【" + job.getJobName() + "】已经在运行！");
    		} else {
				logger.info("任务【" + job.getJobName() + "】已经在运行！");
    		}
			// rescheduleScheduleJob(scheduler, job.getId(), cronExpression);
    		
			// updateScheduleParam(scheduler, job.getId(), paramKey, param);
    	}
    }
    
    
    /**
     * 更新定时任务
     */
	public static void updateScheduleJob(Scheduler scheduler, ScheduleJob scheduleJob) throws SchedulerException {
    	String cronExpression = scheduleJob.getCronExpression();
    	//判断是否存在
    	TriggerKey triggerKey = TriggerKey.triggerKey(scheduleJob.getId());
    	
    	//表达式调度构建器
    	CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression())
    			.withMisfireHandlingInstructionDoNothing();
    	
    	//获取trigger，即在spring配置文件中定义的 bean id="myTrigger"
    	CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
    	
    	//按新的cronExpression表达式重新构建trigger
    	trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
    	
    	//参数
    	trigger.getJobDataMap().put(scheduleJob.JOB_PARAM_KEY+"_"+scheduleJob.getId(), JSON.toJSONString(scheduleJob));
    	
    	scheduler.rescheduleJob(triggerKey, trigger);
    	
    	//暂停任务
    	if((ScheduleStatus.PAUSE.getValue()+"").equals(scheduleJob.getStatus())){
    		pauseScheduleJob(scheduler, scheduleJob);
    	}
    }
    
    /**
	 * 更新参数
	 * 
	 * @param scheduler
	 * @param jobId
	 * @param paramKey
	 * @param param
	 * @throws SchedulerException
	 */
    public static void updateScheduleParam(Scheduler scheduler, String jobId, String paramKey, Object param) throws SchedulerException {
    	//获得任务key
    	JobKey jobKey = JobKey.jobKey(jobId);
    	
    	//获得任务信息
        JobDetail jobDetail = scheduler.getJobDetail(jobKey);
        //jobDetail = jobDetail.getJobBuilder().ofType(jobClass).build();
        
        //重新设置参数
        JobDataMap jobDataMap = jobDetail.getJobDataMap();
        jobDataMap.put(paramKey, param);
        
        //更新参数
        jobDetail.getJobBuilder().usingJobData(jobDataMap);
    }
    
    
    
    
    /**
     * 计划中的任务
     * 注：指那些已经添加到quartz调度器的任务
     * @param scheduler
     * @return
     * @throws SchedulerException
     */
	public static List<ScheduleJob> getPlanScheduleJob(Scheduler scheduler) throws SchedulerException {
    	//获得所有任务组信息
        GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
        
        //获得所有任务Key
        Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
		List<ScheduleJob> jobList = new ArrayList<ScheduleJob>();
        
        //循环所有任务key
        for (JobKey jobKey : jobKeys) {
        	//通过key获得任务触发器
            List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
            
            for (Trigger trigger : triggers) {
				ScheduleJob job = new ScheduleJob();
				job.setId(jobKey.getName());
                job.setRemarks("触发器:" + trigger.getKey());
                
                //获得触发器
                Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
                job.setTriggerStatus(triggerState.name());
                job.setJobStatus(triggerState.name());
                
                if (trigger instanceof CronTrigger) {
                    CronTrigger cronTrigger = (CronTrigger) trigger;
                    String cronExpression = cronTrigger.getCronExpression();
                    job.setCronExpression(cronExpression);
                    job.setNextExecTime(cronTrigger.getNextFireTime());
                }
                jobList.add(job);
            }
        }
        
        return jobList;
    }
    
    /**
     * 运行中的任务
     * @param scheduler
     * @return
     * @throws SchedulerException
     */
	public static List<ScheduleJob> getRunningScheduleJob(Scheduler scheduler) throws SchedulerException {
    	//获得当前正在执行的任务信息
    	List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();
    	
		List<ScheduleJob> jobList = new ArrayList<ScheduleJob>(executingJobs.size());
    	for (JobExecutionContext executingJob : executingJobs) {
			ScheduleJob job = new ScheduleJob();
    	    
    	    //获得任务信息
    	    JobDetail jobDetail = executingJob.getJobDetail();
    	    JobKey jobKey = jobDetail.getKey();
    	    Trigger trigger = executingJob.getTrigger();
			job.setId(jobKey.getName());
    	    job.setRemarks("触发器:" + trigger.getKey());
    	    
    	    //获得触发器
    	    Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
    	    //job.setTriggerStatus(triggerState.name());
    	    job.setJobStatus(triggerState.name());
    	    if (trigger instanceof CronTrigger) {
    	        CronTrigger cronTrigger = (CronTrigger) trigger;
    	        String cronExpression = cronTrigger.getCronExpression();
    	        job.setCronExpression(cronExpression);
    	    }
    	    jobList.add(job);
    	}
    	return jobList;
    }
    
    /**
     * 暂停任务
     * @param scheduler
     * @param scheduleJob
     * @throws SchedulerException
     */
	public static void pauseScheduleJob(Scheduler scheduler, ScheduleJob scheduleJob) throws SchedulerException {
		JobKey jobKey = JobKey.jobKey(scheduleJob.getId());
    	scheduler.pauseJob(jobKey);
    }
    
    /**
     * 恢复任务
     * 注：和暂停任务相对
     * @param scheduler
     * @param scheduleJob
     * @throws SchedulerException
     */
	public static void resumeScheduleJob(Scheduler scheduler, ScheduleJob scheduleJob) throws SchedulerException {
		JobKey jobKey = JobKey.jobKey(scheduleJob.getId());
        scheduler.resumeJob(jobKey);
    }
    
    /**
     * 删除所有任务信息
     * @param scheduler
     * @throws SchedulerException
     */
    public static void deleteAllScheduleJob(Scheduler scheduler) throws SchedulerException {
    	//获得所有任务组信息
        GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
        
        if(null != matcher) {
	        //获得所有任务Key
	        Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
	        
	        if(CollectionUtils.isNotEmpty(jobKeys)) {
		        List<JobKey> keys = new ArrayList<JobKey>();
		        keys.addAll(jobKeys);
		        scheduler.deleteJobs(keys);
	        }
        }
    }
    
    /**
     * 删除任务
     * 注：删除任务后，所对应的trigger也将被删除
     * @param scheduler
     * @param scheduleJob
     * @throws SchedulerException
     */
	public static void deleteScheduleJob(Scheduler scheduler, ScheduleJob scheduleJob) throws SchedulerException {
		JobKey jobKey = JobKey.jobKey(scheduleJob.getId());
        scheduler.deleteJob(jobKey);
    }
    
    /**
     * 立即运行任务
     * 注：这里的立即运行，只会运行一次，方便测试时用
     * @param scheduler
     * @param scheduleJob
     * @throws SchedulerException
     */
	public static void triggerScheduleJob(Scheduler scheduler, ScheduleJob scheduleJob) throws SchedulerException {
		JobKey jobKey = JobKey.jobKey(scheduleJob.getId());
        scheduler.triggerJob(jobKey);
    }
    
    /**
     * 更新任务的时间表达式
	 * 注：更新之后，任务将立即按新的时间表达式执行
     * @param scheduler
     * @param scheduleJob
     * @throws SchedulerException
     */
	public static void rescheduleScheduleJob(Scheduler scheduler, ScheduleJob scheduleJob) throws SchedulerException {
		rescheduleScheduleJob(scheduler, scheduleJob.getId(), scheduleJob.getCronExpression());
    }
    
    /**
     * 更新任务的时间表达式
	 * 注：更新之后，任务将立即按新的时间表达式执行
     * @param scheduler
     * @param scheduleJob
     * @throws SchedulerException
     */
	public static void rescheduleScheduleJob(Scheduler scheduler, String jobId, String cronExpression)
			throws SchedulerException {
		TriggerKey triggerKey = TriggerKey.triggerKey(jobId);
    	
		//获取trigger，即在spring配置文件中定义的 bean id="myTrigger"
		CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
    	
    	//如果不存在，则不需要修改；否则，修改参数与表达式
    	if(null != trigger) {    	
			//表达式调度构建器
			CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
			
			scheduleBuilder.inTimeZone(DEFAULT_TIME_ZONE);
			 
			//按新的cronExpression表达式重新构建trigger
			trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
			    .withSchedule(scheduleBuilder).build();
			 
			//按新的trigger重新设置job执行
			scheduler.rescheduleJob(triggerKey, trigger);
    	}
    }
}
